测试环境rpcclient动态调用RPC服务实践
作者|申言方
随着业务的增长,转转QA团队开发了一系列测试工具,随着代码量的增加和工具的增多,商业测试平台应运而生。平台的愿景是提高QA测试效率,帮助RD实现自测,但实际使用发现了一些问题:
测试平台只能加载一个rpc服务的配置文件;
测试平台加载的rpc服务配置文件无法实时变更,做到因人而异;
测试平台不能够并发调用不同机器上的rpc服务,“各用个的”互不影响。
以上3个问题,究其根本原因是作为测试平台的WF站点,不能根据需要调用指定机器上的RPC服务,即rpcclient在线下环境不能动态调用不同机器上的RPC服务。
释疑:转转有完整的测试环境网络隔离,所有测试用机器统一在环境管理平台上管理,大致分为测试环境、稳定环境、沙箱环境等,而这些环境中的RPC服务,无法注册并使用线上的服务管理平台。关于环境平台详见转转QA公众号前述分享:转转测试环境平台解决方案
1、JAVA类加载机制
主要有 父类优先策略 和 子类优先策略,顾名思义父类优先策略,当前classloader加载类时,先委托父类去加载,形成一个类加载的链路。子类优先策略,则是首先当前classloader在自己的classpath中寻找class文件。
2、类加载同名类的处理
在同一个cloassloader中,加载classpath中靠前的类。
3、JAVA动态代理
java.lang.reflect.*系列提供的功能
4、ThreadLocal
java.lang.ThreadLocal
测试平台先来看一下商业测试平台目前的实现方式,以创建商品并投放为例,在提交之前,需要手动选择一下需要调用的rpc服务所在机器的ip,这个下拉列表是调用环境平台的api,获取当前用户拥有操作权限的所有机器,再加上默认的测试稳定环境、沙箱稳定环境。
加上其他必填的参数后,点击提交,页面上就会展示本次请求的执行结果
wf站点的日志中可以看到本次请求所调用到的rpc服务所在的ip
针对背景中提到的3个问题,测试平台通过接入公司内网sso,调用环境平台对应api,实时获取ip列表,并动态生成rpc服务配置文件来解决问题2;问题1、3需要修改rpcclient来实现。
rpcclient动态调用RPC服务为了实现“rpcclient动态调用RPC服务”这一功能,rpcclient需要实现加载多个rpc.config,我用两种方式分别针对不同的rpcclient版本,实现这一功能。
方案1
低版本的rpcclient调用逻辑见下图,主要就是使用了java的动态代理机制,来反射生成需要调用的rpc服务,通过解析默认位置的rpc服务配置文件,来确定rpc服务所在的机器及端口,通过序列化和反序列化来传递调用参数和返回结果。
需要做的改动
配置文件的生成
单独的api提供,在测试平台上选择需要更新的rpc服务所在的ip,点击更新后,根据环境平台api返回的该ip上所有已部署的集群,生成新的配置文件,该操作会覆盖已有对应的配置文件。文件名称以ip作为前缀,例如:192.168.187.208_rpc.config
配置文件的加载
从Class1的create方法增加一个入参 ---- 需要调用的rpc服务所在机器的ip,然后一直传递到Class5的get()方法,get()通过ip来解析对应rpc服务配置文件,返回响应的rpc服务ip和端口等信息
新建工程,把修改后的代码打包成1.dynamic.client.jar,这样classloader在加载时,会优先加载1.dynamic.client.jar中的类,就不会再加载rpcclient.jar中重名的类。虽然java加载jar的先后顺序是由操作系统决定,但是在linux上每次都全量更新lib下的所有jar包,目前还没有出现先加载rpcclient.jar的情况。
这个方案有一些缺点:
已有代码改动较大
代码依赖比较严重,如果不想使用rpcclient动态加载rpc服务这一功能,需要再改回原来的代码
每个create相关的调用都需要改,工作量有些大
方案2
新版的rpcclient改变了client类之间的调用环节,但是最后还是需要getConfig()方法来加载和解析rpc.config,为了一并解决方案1中的缺点,本方案使用了ThreadLocal来实现,调用过程简略示意图如下:
使用该方案,原来的代码不用做太大改动,直接在拦截器中添加相应代码就可以,解决了方案1的缺陷,并保证了功能的实现。
实现动态修改rpc.config,不用再重启wf站点就可以生效
每个用户使用自己的rpc.config,多个用户之间互不干扰
不需要修改本机host
解决了平台型工具需要多个机器部署的尴尬,不用在被测服务的机器上再启动一个测试平台
请求用到的server信息可追踪
RD自测,可使用QA提供的测试代码
为后续RPC服务diff奠定基础
如有必要 RPC mock也可以实现